﻿using System;
using System.Globalization;
using System.Threading;
using System.Windows;
using System.Runtime.Serialization;
using System.Linq;

namespace MesopotamiaLauncher
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App
    {
        private readonly object _syncObject = new object();
        private readonly DateTime _startTime = DateTime.Now;
        private bool _checkingHB;
        private System.Diagnostics.Process _mesopotamia;
        private Timer _hbCheck;
        private int _port = 50000;

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            MiniLogger.LogDebug("OnStartup");

            var args = e.Args;
            if (args.Length > 0)
            {
                // Get the command line argument
                var arg = args[0].ToLower(CultureInfo.InvariantCulture);

                string configPath = null;
                if (args.Length == 2)
                    configPath = args[1];

                MiniLogger.LogDebug("Switch : " + arg);
                switch (arg)
                {
                    case "/c":
                        // Show the options dialog
                        //Settings settings = new Settings();
                        //settings.Show();
                        break;
                    case "/p":
                        // Don't do anything for preview
                        Shutdown();
                        break;
                    case "/s":
                        // Show screensaver form
                        ShowScreensaver(configPath);
                        break;
                    case "/pw":
                        ShowPW(configPath);
                        break;
                    case "/cmdline":
                        ShowCmdLine(configPath);
                        break;
                    default:
                        Shutdown();
                        break;
                }
            }
            else
            {
                // If no arguments were passed in, show the screensaver
                ShowScreensaver(null);
            }
        }

        private void ShowPW(string configPath)
        {
            var mesFiles = new MesoptamiaFiles();

            if (string.IsNullOrEmpty(configPath))
                configPath = System.IO.Path.Combine(Environment.CurrentDirectory, MesopotamiaLauncherInstaller.ConfigFile);



            mesFiles = LoadConfig(configPath, mesFiles);
            mesFiles.MesopotamiaExecutable = "PW.exe";

            StartMesopotamia(mesFiles, null);
        }

        private void ShowCmdLine(string configPath)
        {
            var mesFiles = new MesoptamiaFiles();

            if (string.IsNullOrEmpty(configPath))
                configPath = System.IO.Path.Combine(Environment.CurrentDirectory, MesopotamiaLauncherInstaller.ConfigFile);

            mesFiles = LoadConfig(configPath, mesFiles);
            mesFiles.MesopotamiaExecutable = "MesopotamiaCmdLine.exe";

            StartMesopotamia(mesFiles, null);
        }

        private void ShowScreensaver(string configPath)
        {
            var mesFiles = new MesoptamiaFiles();

            if (string.IsNullOrEmpty(configPath))
                configPath = System.IO.Path.Combine(Environment.CurrentDirectory, MesopotamiaLauncherInstaller.ConfigFile);

            if (!System.IO.File.Exists(configPath))
            {
                configPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System),
                                                    MesopotamiaLauncherInstaller.ConfigFile);
            }

            mesFiles = LoadConfig(configPath, mesFiles);

            StartMesopotamia(mesFiles, " /s ");

        }

        private static MesoptamiaFiles LoadConfig(string configFile, MesoptamiaFiles mesFiles)
        {
            try
            {
                MiniLogger.LogDebug(configFile);

                // load the config));
                using (System.IO.Stream sw = new System.IO.FileStream(configFile, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    var dataContractSerializer = new DataContractSerializer(mesFiles.GetType());
                    mesFiles = (MesoptamiaFiles)dataContractSerializer.ReadObject(sw);
                }

            }
            catch (Exception ex)
            {
                MiniLogger.LogDebug(ex.Message);
            }
            return mesFiles;
        }

        /// <summary>
        /// Shows the screen-saver by creating an instance on Main.xaml per screen.
        /// </summary>
        private void StartMesopotamia(MesoptamiaFiles mesoptamiaFiles, string arguments)
        {

            try
            {
                MiniLogger.LogDebug(mesoptamiaFiles.ToString());

                // launch the process
                _mesopotamia = new System.Diagnostics.Process
                                      {
                                          StartInfo =
                                              {
                                                  WorkingDirectory = mesoptamiaFiles.MesopotamiaPath,
                                                  FileName =
                                                      System.IO.Path.Combine(mesoptamiaFiles.MesopotamiaPath,
                                                                             mesoptamiaFiles.MesopotamiaExecutable),
                                              }
                                      };

                MiniLogger.LogDebug("Working Directory : " + mesoptamiaFiles.MesopotamiaPath);
                MiniLogger.LogDebug("Exectutable : " + mesoptamiaFiles.MesopotamiaExecutable);

                _mesopotamia.StartInfo.UseShellExecute = false;

                bool restart;
                var restartCount = -1;
                do
                {
                    restartCount++;
                    _mesopotamia.StartInfo.Arguments = "/runtime:" + _startTime.Ticks + " /port:" + _port + arguments;
                    MiniLogger.LogDebug("Starting with arguments " + _mesopotamia.StartInfo.Arguments);
                    _mesopotamia.Start();
                    TimerStart();
                    _mesopotamia.WaitForExit();
                    TimerStop();
                    MiniLogger.LogDebug("Stopped");
                    MiniLogger.LogDebug("Exit Code : " + _mesopotamia.ExitCode);
                    MiniLogger.LogDebug("Restart Count : " + restartCount);
                    restart = _mesopotamia.ExitCode != 0;

                    if (restart)
                        KillWarningMessages();

                    if (_mesopotamia.ExitCode == -2)
                    {
                        _port += 2;
                    }


                } while (restart);
            }
            catch (Exception ex)
            {
                MiniLogger.LogDebug("Error starting Mesopotamia : " + ex.Message);
            }
            finally
            {
                Environment.Exit(0);
            }
        }

        private void TimerStart()
        {
            _hbCheck = new Timer(CheckHB, _mesopotamia, new TimeSpan(0, 0, 10), new TimeSpan(0, 0, 5));
        }

        private void TimerStop()
        {
            if (_hbCheck != null)
            {
                _hbCheck.Dispose();
                _hbCheck = null;
            }
        }

        private void TimerRestart()
        {
            TimerStop();
            TimerStart();
        }

        private void CheckHB(object mesopotamia)
        {
            lock (_syncObject)
            {
                if (_checkingHB)
                    return;

                try
                {
                    _checkingHB = true;

                    var process = ((System.Diagnostics.Process)mesopotamia);

                    process.Refresh();

                    if (process.MainWindowTitle.Contains("Welcome"))
                    {
                        TimerRestart();
                        return;
                    }

                    if (IsProcessResponding(process))
                    {
                        MiniLogger.LogDebug("App possibly not responding, starting check of status");

                        TimerStop();

                        Thread.Sleep(new TimeSpan(0, 0, 60));

                        process.Refresh();

                        if (!IsProcessResponding(process) || (process.HasExited && process.ExitCode == 0))
                        {
                            TimerRestart();
                            return;
                        }

                        MiniLogger.LogDebug("process.Responding : " + process.Responding);
                        MiniLogger.LogDebug("process.MainWindowTitle.Length : " + process.MainWindowTitle.Length);
                        MiniLogger.LogDebug(string.Format("Killing process {0} after short sleep", process.Id));
                        process.Kill();
                        process.Refresh();
                        if (process.HasExited)
                        {
                            MiniLogger.LogDebug(string.Format("Exit for process {0} with exit Code : {1}  ", process.Id, process.ExitCode));
                        }
                        else
                        {
                            MiniLogger.LogDebug(string.Format("Warning process {0}  hasnt exited yet", process.Id));
                        }

                        KillWarningMessages();
                    }
                }
                catch (Exception ex)
                {
                    MiniLogger.LogDebug(ex.Message);
                }
                finally
                {
                    _checkingHB = false;
                }

            }
        }

        private static void KillWarningMessages()
        {
            var warningWindows = System.Diagnostics.Process.GetProcesses().Where(p => p.MainWindowTitle == "Mesopotamia" || string.IsNullOrEmpty(p.MainWindowTitle));

            foreach (var window in warningWindows.Where( window => window.ProcessName.ToLower().Contains("werfault") || 
                window.ProcessName.ToLower().Contains("dw20") || window.ProcessName.ToLower().Contains("dwwin")))
            {
                try
                {
                    MiniLogger.LogDebug("Killing Fault Window : " + window.ProcessName);
                    window.Kill();
                }
                catch (Exception ex)
                {

                    MiniLogger.LogDebug("Error KillWarningMessages : " + ex.Message); 
                }
            }
        }

        private static bool IsProcessResponding(System.Diagnostics.Process process)
        {
            process.Refresh();
            return ((!process.Responding && !process.HasExited) || (process.MainWindowTitle.Length == 0));
        }
    }

    [DataContract]
    public class MesoptamiaFiles
    {
        [DataMember]
        public string MesopotamiaExecutable;
        [DataMember]
        public string MesopotamiaPath;

        public override string ToString()
        {
            var result = "";

            result += "Path : " + MesopotamiaPath;
            result += " Exectuable : " + MesopotamiaExecutable;

            return result;
        }
    }
}
